#include <fstream>
#include <string>
#include <map>
#include "prop_parser.h"

static std::map<std::string, std::string> data;
static unsigned long long line = 1;
static unsigned long long pos = 0;

std::string trim(std::string str) {
  int i, j;
  for (i = 0; i < str.length(); i++) {
    if (str[i] != ' ' && str[i] != '\t') {
      break;
    }
  }
  for (j = str.length() - 1; j >= 0; j--) {
    if (str[j] != ' ' && str[j] != '\t') {
      break;
    }
  }
  return str.substr(i, j - i + 1);
}

std::string parseException() {
  std::string s1("parse line: ");
  std::string s2 = "", s4 = "";
  std::string s3(", position: ");
  std::string s5(" failed");
  while (line > 0) {
    s2 += (char)(line % 10 + 0x30);
    line /= 10;
  }
  while (pos > 0) {
    s4 += (char)(pos % 10 + 0x30);
    pos /= 10;
  }
  return s1 + s2 + s3 + s4 + s5;
}

std::string toNumberException(std::string str) {
  return "Cannot parse " + str + " as a number";
}

void fr::parseFile(std::string fileName) {
  std::ifstream file(fileName, std::ios::in | std::ios::binary);
  if (!file) {
    throw std::string("read ") + fileName + std::string(" failed");
  }
  int ch;
  std::string str = "";
  while ((ch = file.get()) != EOF) {
    str += (char)ch;
  }
  parseString(str);
}

void fr::parseString(std::string str) {
  int status = 0;
  std::string key = "";
  std::string value = "";
  line = 1;
  pos = 0;
  for (auto ch : str) {
    pos++;
    switch (status) {
      case 0: {
        if (ch == '=') {
          if (key == "") {
            status = -1;
            goto exception;
          } else {
            status = 1;
          }
        } else if (ch == '#') {
          status = -1;
          goto exception;
        } else {
          key += ch;
        }
        break;
      }
      case 1: {
        if (ch != '\n' && ch != '#') {
          value += ch;
        } else {
          data.emplace(trim(key), trim(value));
          key = "";
          value = "";
          if (ch == '#') {
            status = 2;
          } else {
            line++;
            status = 0;
            pos = 0;
          }
        }
        break;
      }
      case 2: {
        if (ch == '\n') {
          line++;
          pos = 0;
          status = 0;
        }
        break;
      }
    }
  }
exception:
  if (status == -1) {
    throw parseException();
  }
  if (key != "" && value == "") {
    throw parseException();
  }
  data.emplace(trim(key), trim(value));
}

std::string fr::get(std::string key) {
  return data[key];
}

long long fr::getNumber(std::string key) {
  auto value = data[key];
  long long number = 0;
  int isNeg = 1;
  int status = 0;
  bool canExit = true;
  for (auto ch : value) {
    switch (status) {
      case 0: {
        if (ch == '-') {
          isNeg = -1;
          status = 1;
          canExit = false;
        } else if (ch == '0') {
          status = 2;
          canExit = true;
        } else if (ch > '0' && ch <= '9') {
          number = number * 10 + ch - '0';
          status = 6;
          canExit = true;
        } else {
          throw toNumberException(value);
        }
        break;
      }
      case 1: {
        canExit = true;
        if (ch == '0') {
          status = 2;
        } else if (ch > '0' && ch <= '9') {
          number = number * 10 + ch - '0';
          status = 6;
        } else {
          throw toNumberException(value);
        }
        break;
      }
      case 2: {
        if (ch == 'x' || ch == 'X') {
          status = 3;
          canExit = false;
        } else if (ch == 'b' || ch == 'B') {
          status = 4;
          canExit = false;
        } else if (ch >= '0' && ch <= '7') {
          number = number * 8 + ch - '0';
          status = 5;
          canExit = true;
        } else {
          throw toNumberException(value);
        }
        break;
      }
      case 3: {
        canExit = true;
        if (ch >= '0' && ch <= '9') {
          number = number * 16 + ch - '0';
        } else if (ch == 'a' || ch == 'A') {
          number = number * 16 + 10;
        } else if (ch == 'b' || ch == 'B') {
          number = number * 16 + 11;
        } else if (ch == 'c' || ch == 'C') {
          number = number * 16 + 12;
        } else if (ch == 'd' || ch == 'D') {
          number = number * 16 + 13;
        } else if (ch == 'e' || ch == 'E') {
          number = number * 16 + 14;
        } else if (ch == 'f' || ch == 'F') {
          number = number * 16 + 15;
        } else {
          throw toNumberException(value);
        }
        break;
      }
      case 4: {
        canExit = true;
        if (ch == '0' || ch == '1') {
          number = number * 2 + ch - '0';
        } else {
          throw toNumberException(value);
        }
        break;
      }
      case 5: {
        canExit = true;
        if (ch >= '0' && ch <= '7') {
          number = number * 8 + ch - '0';
        } else {
          throw toNumberException(value);
        }
        break;
      }
      case 6: {
        canExit = true;
        if (ch >= '0' && ch <= '9') {
          number = number * 10 + ch - '0';
        } else {
          throw toNumberException(value);
        }
        break;
      }
    }
  }
  if (!canExit) {
    throw toNumberException(value);
  }
  return number * isNeg;
}
